#include "timer.hpp"

#include "timer_engine.hpp"

#include <atomic>

namespace DcAdr
{

    constexpr std::size_t kInvalidTimerId = std::size_t(-1);

    // -----------------------------------------

    class TimerPrivate
    {
    public:
        TimerPrivate();
        ~TimerPrivate();

    public:
        TimerEngine &timer_engine_{g_global_timer_engine};

        bool single_shot_{false};              // 是否单次定时
        std::int64_t interval_{0};             // 定时器间隔
        std::size_t id_{kInvalidTimerId};      // 最小堆中的序号，随时会变
        std::atomic<std::int64_t> expires_{0}; // 超时到达时间
        std::function<void()> slot_;           // 超时回调函数
    };

    TimerPrivate::TimerPrivate()
    {
    }

    TimerPrivate::~TimerPrivate()
    {
    }

    // -------------------------------------------------

    Timer::Timer()
        : pri_(new TimerPrivate())
    {
    }

    Timer::Timer(bool single_shot)
        : Timer()
    {
        SetSingleShot(single_shot);
    }

    Timer::~Timer()
    {
        Stop();
    }

    void Timer::SetSingleShot(bool single_shot)
    {
        pri_->single_shot_ = single_shot;
    }

    bool Timer::IsSingleShot() const
    {
        return pri_->single_shot_;
    }

    void Timer::SetInterval(std::int64_t intreval)
    {
        pri_->interval_ = intreval;
    }

    std::int64_t Timer::GetInterval() const
    {
        return pri_->interval_;
    }

    bool Timer::IsActive() const
    {
        return pri_->id_ != kInvalidTimerId;
    }

    void Timer::Start(const std::function<void()> &slot)
    {
        Stop();

        pri_->slot_ = slot;
        pri_->expires_ = pri_->interval_ + TimerEngine::GetCurrentMillisecs();

        pri_->timer_engine_.AddTimer(this, true);
    }

    void Timer::Start(std::int64_t interval, const std::function<void()> &slot)
    {
        Stop();

        pri_->interval_ = interval;
        pri_->slot_ = slot;
        pri_->expires_ = pri_->interval_ + TimerEngine::GetCurrentMillisecs();

        pri_->timer_engine_.AddTimer(this, true);
    }

    void Timer::Stop()
    {
        if (IsActive())
        {
            pri_->timer_engine_.RemoveTimer(this, true);
            pri_->id_ = kInvalidTimerId;
        }
    }

    void Timer::Reset()
    {
        pri_->expires_ = pri_->interval_ + TimerEngine::GetCurrentMillisecs();
    }

    std::int64_t Timer::GetRemainingTime() const
    {
        if (!IsActive())
        {
            return -1;
        }
        std::int64_t time_now = TimerEngine::GetCurrentMillisecs();
        if (time_now < pri_->expires_)
        {
            return 0;
        }
        else
        {
            return time_now - pri_->expires_;
        }
    }

    std::size_t &Timer::GetIdRef()
    {
        return pri_->id_;
    }

    std::int64_t Timer::GetExpires() const
    {
        return pri_->expires_;
    }

    void Timer::OnTimeout(std::int64_t time_now)
    {
        if (IsSingleShot())
        {
            pri_->id_ = kInvalidTimerId;
        }
        else
        {
            pri_->expires_ = pri_->interval_ + time_now;
            pri_->timer_engine_.AddTimer(this, false);
        }

        if (pri_->slot_)
        {
            pri_->slot_();
        }
    }

} // namespace DcAdr